package hr.ctg.domain.action;

import hr.ctg.domain.model.DnevnikAktivnosti;
import hr.ctg.domain.model.Dogadjaj;
import hr.ctg.domain.model.Korisnik;
import hr.ctg.domain.model.Mjesto;
import hr.ctg.domain.model.Objekt;
import hr.ctg.domain.model.Otok;
import hr.ctg.domain.model.Park;
import hr.ctg.domain.model.Slika;
import hr.ctg.service.dao.AkcijaDAO;
import hr.ctg.service.dao.DnevnikAktivnostiDAO;
import hr.ctg.service.dao.KorisnikDAO;
import hr.ctg.service.dao.MjestoDAO;
import hr.ctg.service.dao.ObjektDAO;
import hr.ctg.service.dao.OtokDAO;
import hr.ctg.service.dao.ParkDAO;
import hr.ctg.service.dao.SlikaDAO;
import hr.ctg.util.CacheUtil;

import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.faces.context.FacesContext;
import javax.imageio.ImageIO;
import javax.persistence.EntityManager;
import javax.servlet.ServletContext;
import javax.swing.ImageIcon;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.annotations.web.RequestParameter;
import org.jboss.seam.cache.CacheProvider;
import org.jboss.seam.log.Log;
import org.richfaces.event.UploadEvent;
import org.richfaces.model.UploadItem;

@Name("slikaAction")
@Scope(ScopeType.CONVERSATION)
public class SlikaAction {
	private static final int MAX_IMAGE_WIDTH = 800;
	private static final int MIN_IMAGE_RESIZE_WIDTH = 700;
	private static final int MAX_IMAGE_RESIZE_HEIGHT = 600;
	// private static final int MAX_IMAGE_HEIGHT = 600;
	private static Object[][] size = { { MAX_IMAGE_WIDTH, -1, "" },
			{ 290, 190, "m" }, { 140, 95, "s" } };
	@In
	EntityManager entityManager;

	@In
	CacheProvider cacheProvider;
	@In
	SlikaDAO slikaDAO;
	@In
	ObjektDAO objektDAO;
	@In
	MjestoDAO mjestoDAO;
	@In
	OtokDAO otokDAO;
	@In
	KorisnikDAO korisnikDAO;
	@In
	ParkDAO parkDAO;
	@In
	String currentLang;
	@In(required = false)
	FacesContext facesContext;

	@In
	AkcijaDAO akcijaDAO;

	@In
	DnevnikAktivnostiDAO dnevnikAktivnostiDAO;

	@In(required = false)
	Korisnik korisnik;

	@Logger
	Log log;

	@RequestParameter
	Integer idObjekta;
	@RequestParameter
	Integer idMjesta;
	@RequestParameter
	Integer idOtoka;
	@RequestParameter
	Integer idParka;

	private List<Slika> uneseneSlike;
	private Objekt objekt;
	private Mjesto mjesto;
	private Otok otok;
	private Park park;
	private Slika slika;
	private String back;
	private String tipCilja;
	private String nazivCilja;
	
	private boolean otherUserImagesPresent = false;

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getSlikeByObjekt()
	 */
	@Factory("slikeByObjekt")
	public List<Slika> getSlikeByObjekt() {
		return slikaDAO.findByIdObjekta(idObjekta);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getSlikeByMjesto()
	 */
	@Factory("slikeByMjesto")
	public List<Slika> getSlikeByMjesto() {
		return slikaDAO.findByIdMjesta(idMjesta);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getSlikeByOtok()
	 */
	@Factory("slikeByOtok")
	public List<Slika> getSlikeByOtok() {
		return slikaDAO.findByIdOtoka(idOtoka);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getSlikeByPark()
	 */
	@Factory("slikeByPark")
	public List<Slika> getSlikeByPark() {
		return slikaDAO.findByIdParka(idParka);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getSlika(int)
	 */
	public Slika getSlika(final int idSlike) {
		Slika s = null;

		s = (Slika) cacheProvider.get("slike", "idSlike_" + idSlike);

		if (s == null) {
			s = slikaDAO.find(idSlike);
			if (s != null) {
				cacheProvider.put("slike", "idSlike_" + idSlike, s);
			}
		}
		return s;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getPocSlikaByObjekt()
	 */
	@Factory("pocSlikaByObjekt")
	public Slika getPocSlikaByObjekt() {
		return slikaDAO.findByIdObjektaP(idObjekta);
	}

	public String getPocSlikaPath(Object o, String size) {
		Slika s;
		if (o instanceof Objekt) {
			s = slikaDAO.findByIdObjektaP(((Objekt) o).getIdObjekta());
		} else if (o instanceof Mjesto) {
			s = slikaDAO.findByIdMjestaP(((Mjesto) o).getIdMjesta());
		} else if (o instanceof Otok) {
			s = slikaDAO.findByIdOtokaP(((Otok) o).getIdOtoka());
		} else if (o instanceof Park) {
			s = slikaDAO.findByIdParkaP(((Park) o).getIdParka());
		} else {
			s = slikaDAO.findByIdDogadjajaP(((Dogadjaj) o).getIdDogadjaja());
		}

		String imagePath;
		if (s != null) {
			imagePath = "/static/images/" + s.getImeSlike(size);
		} else {
			imagePath = "/main" + Slika.NO_IMAGE_PATH;
		}
		return imagePath;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getPocSlikaByPark()
	 */
	@Factory("pocSlikaByPark")
	public Slika getPocSlikaByPark() {
		return slikaDAO.findByIdParkaP(idParka);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getPocSlikaByMjesto()
	 */
	@Factory("pocSlikaByMjesto")
	public Slika getPocSlikaByMjesto() {
		return slikaDAO.findByIdMjestaP(idMjesta);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getPocSlikaByOtok()
	 */
	@Factory("pocSlikaByOtok")
	public Slika getPocSlikaByOtok() {
		return slikaDAO.findByIdOtokaP(idOtoka);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#addSlika(java.lang.String)
	 */
	@Begin(join = true)
	@Restrict("#{identity.loggedIn}")
	public String addSlika(final String back) {
		this.back = back;
		slika = new Slika();
		log.debug("Zapocinjem korisnicki prijedlog slike...", new Object[0]);
		if (idObjekta != null) {
			objekt = objektDAO.find(idObjekta);
			nazivCilja = objekt.getNaziv();
			tipCilja = "" + objekt.getTipObjekta().getIdTipaObjekta();
		} else if (idMjesta != null) {
			mjesto = mjestoDAO.find(idMjesta);
			nazivCilja = mjesto.getNaziv();
			tipCilja = "label.place";
		} else if (idParka != null) {
			park = parkDAO.find(idParka);
			nazivCilja = park.getNaziv();
			tipCilja = park.getTip().charValue() == 'P' ? "title.nature.park"
					: "title.national.park";
		} else {
			otok = otokDAO.find(idOtoka);
			nazivCilja = otok.getNaziv();
			tipCilja = "label.otok";
		}

		if (objekt != null && objekt.getSlikas().size() > 0) {
			uneseneSlike = new ArrayList<Slika>(objekt.getSlikas());
		} else if (mjesto != null && mjesto.getSlikas().size() > 0) {
			uneseneSlike = new ArrayList<Slika>(mjesto.getSlikas());
		} else if (otok != null && otok.getSlikas().size() > 0) {
			uneseneSlike = new ArrayList<Slika>(otok.getSlikas());
		} else if (park != null && park.getSlikas().size() > 0) {
			uneseneSlike = new ArrayList<Slika>(park.getSlikas());
		} else {
			uneseneSlike = new ArrayList<Slika>();
		}
		if(uneseneSlike.size()>0)otherUserImagesPresent=true;
		for (Slika slika : uneseneSlike) {
			readImageFromFileSystem(slika);
		}

		return "addSlika";
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#azurirajSlike()
	 */
	@End(beforeRedirect = true)
	@Transactional
	public String azurirajSlike() {
		if (uneseneSlike.size() > 0) {
			if (objekt != null) {
				objekt.getSlikas().addAll(uneseneSlike);
			} else if (mjesto != null) {
				mjesto.getSlikas().addAll(uneseneSlike);
			} else if (park != null) {
				park.getSlikas().addAll(uneseneSlike);
			} else {
				otok.getSlikas().addAll(uneseneSlike);
			}

		}

		for (Slika slika : uneseneSlike) {
			if(!otherUserImagesPresent){
				slika.setPocetna(true);
				otherUserImagesPresent=true;
			}
			slikaDAO.persist(slika);
			slikaDAO.flush();
			saveImageToFileSystem(slika);
		}

		log.debug("Registriram akciju u dnevniku aktivnosti objekt....",
				new Object[0]);
		final DnevnikAktivnosti da = new DnevnikAktivnosti();
		da.setKorisnik(korisnik);
		da.setOpis("Azuriranje kroz free web formu");
		da.setVrijeme(new Date());
		if (objekt != null) {
			da.setObjekt(objekt);
		} else if (mjesto != null) {
			da.setMjesto(mjesto);
		} else if (park != null) {
			da.setPark(park);
		} else {
			da.setOtok(otok);
		}

		log.debug("Definiram akciju UPDATE....", new Object[0]);
		da.setAkcija(akcijaDAO.find(new Integer(2)));
		dnevnikAktivnostiDAO.persist(da);

		if (objekt != null) {
			CacheUtil.clearCache(objekt, currentLang);
		}

		if (mjesto != null) {
			CacheUtil.clearCache(mjesto, currentLang);
		}
		if (otok != null) {
			CacheUtil.clearCache(otok, currentLang);
		}
		if (park != null) {
			CacheUtil.clearCache(park, currentLang);
		}

		return back;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#addSlika()
	 */
	public void addSlika() {
		log.debug("Unosim sliku " + slika, new Object[0]);
		if (slika.getSadrzaj() == null || slika.getSadrzaj().length == 0) {
			slika = new Slika();
			return;
		}
		if (uneseneSlike.size() == 0) {
			slika.setPocetna(true);
		}

		final Korisnik autor = korisnik;
		slika.setKorisnik(autor);
		if (objekt != null) {
			slika.setObjekt(objekt);
		} else if (mjesto != null) {
			slika.setMjesto(mjesto);
		} else if (park != null) {
			slika.setPark(park);
		} else {
			slika.setOtok(otok);
		}

		uneseneSlike.add(slika);
		slika = new Slika();
		FacesContext.getCurrentInstance().renderResponse();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * hr.ctg.domain.action.SlikaAction#ukloniSliku(hr.ctg.domain.model.Slika)
	 */
	public void ukloniSliku(final Slika zaUkloniti) {
		log.debug("Uklanjam: " + zaUkloniti.getOpis(), new Object[0]);
		uneseneSlike.remove(zaUkloniti);
		if (objekt != null) {
			if (objekt.getSlikas().contains(zaUkloniti)) {
				objekt.getSlikas().remove(zaUkloniti);
				slikaDAO.remove(zaUkloniti);
			}
		} else if (mjesto != null) {
			if (mjesto.getSlikas().contains(zaUkloniti)) {
				mjesto.getSlikas().remove(zaUkloniti);
				slikaDAO.remove(zaUkloniti);
			}
		} else if (park != null) {
			if (park.getSlikas().contains(zaUkloniti)) {
				park.getSlikas().remove(zaUkloniti);
				slikaDAO.remove(zaUkloniti);
			}
		} else {
			if (otok.getSlikas().contains(zaUkloniti)) {
				otok.getSlikas().remove(zaUkloniti);
				slikaDAO.remove(zaUkloniti);
			}
		}

		deleteImageFromFileSystem(zaUkloniti);

		if (zaUkloniti.isPocetna() && uneseneSlike.size() > 0) {
			uneseneSlike.get(0).setPocetna(true);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getSlika()
	 */
	public Slika getSlika() {
		return slika;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#setSlika(hr.ctg.domain.model.Slika)
	 */
	public void setSlika(final Slika slika) {
		this.slika = slika;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getUneseneSlike()
	 */
	public List<Slika> getUneseneSlike() {
		return uneseneSlike;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * hr.ctg.domain.action.SlikaAction#setUneseneSlike(java.util.ArrayList)
	 */
	public void setUneseneSlike(final ArrayList<Slika> uneseneSlike) {
		this.uneseneSlike = uneseneSlike;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#back()
	 */
	@End(beforeRedirect = true)
	public String back() {
		return back;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * hr.ctg.domain.action.SlikaAction#onFileUploadListener(org.richfaces.event
	 * .UploadEvent)
	 */
	public void onFileUploadListener(final UploadEvent e) {
		final Slika slika = new Slika();
		slika.setSadrzaj(e.getUploadItem().getData());
		slika.setTipSadrzaja(e.getUploadItem().getContentType());
		log.debug("Unosim sliku " + slika, new Object[0]);
		if (slika.getSadrzaj() == null || slika.getSadrzaj().length == 0) {
			return;
		}
		if (uneseneSlike.size() == 0) {
			slika.setPocetna(true);
		}

		final Korisnik autor = korisnik;
		slika.setKorisnik(autor);
		if (objekt != null) {
			slika.setObjekt(objekt);
		} else if (mjesto != null) {
			slika.setMjesto(mjesto);
		} else if (park != null) {
			slika.setPark(park);
		} else {
			slika.setOtok(otok);
		}

		uneseneSlike.add(slika);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getTipCilja()
	 */
	public String getTipCilja() {
		return tipCilja;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see hr.ctg.domain.action.SlikaAction#getNazivCilja()
	 */
	public String getNazivCilja() {
		return nazivCilja;
	}

	public void processSlika(final UploadEvent ue) {
		log.debug("Unosim slike...");
		for (final UploadItem ui : ue.getUploadItems()) {
			final Slika slika = new Slika();
			slika.setSadrzaj(ui.getData());
			slika.setTipSadrzaja(ui.getContentType());
			log.debug("Unosim sliku " + slika, new Object[0]);
			if (slika.getSadrzaj() == null || slika.getSadrzaj().length == 0) {
				return;
			}
			if (uneseneSlike.size() == 0) {
				slika.setPocetna(true);
			}

			final Korisnik autor = korisnik;
			slika.setKorisnik(autor);
			if (objekt != null) {
				slika.setObjekt(objekt);
			} else if (mjesto != null) {
				slika.setMjesto(mjesto);
			} else if (park != null) {
				slika.setPark(park);
			} else {
				slika.setOtok(otok);
			}

			uneseneSlike.add(slika);
		}
		log.debug("Prisutno slika: " + uneseneSlike.size());
	}

	public void saveImageToFileSystem(Slika s) {
		byte[] data;
		for (int i = 0; i < size.length; i++) {
			data = resizeImage(s.getSadrzaj(), s.getTipSadrzaja(),
					(Integer) size[i][0], (Integer) size[i][1]);
			String path = getImageFolderRealPath();
			path += File.separator
					+ s.getIdSlike()
					+ size[i][2]
					+ "."
					+ s.getTipSadrzaja().substring(
							s.getTipSadrzaja().indexOf("/") + 1,
							s.getTipSadrzaja().length());

			File sf = new File(path);
			BufferedOutputStream bos = null;
			try {
				bos = new BufferedOutputStream(new FileOutputStream(sf));
				bos.write(data);

			} catch (Exception e) {
				log.error(e.getMessage(), e);
			} finally {
				try {
					bos.close();
				} catch (IOException e) {
				}
			}

		}
	}

	public void readImageFromFileSystem(Slika s) {
		String path = getImageFolderRealPath();
		path += File.separator + s.getImeSlike("");
		File sf = new File(path);
		byte[] data = null;
		BufferedInputStream bos = null;

		try {
			bos = new BufferedInputStream(new FileInputStream(sf));
			data = new byte[(int) sf.length()];
			bos.read(data);
			s.setSadrzaj(data);
			bos.close();
		} catch (FileNotFoundException e) {
			log.error(e.getMessage());
			path = getThemeImageFolderRealPath();
			path += File.separator + "no_image_corrupted.png";
			sf = new File(path);
			try {
				bos = new BufferedInputStream(new FileInputStream(sf));
				data = new byte[(int) sf.length()];
				try {
					bos.read(data);
					s.setSadrzaj(data);
					bos.close();
				} catch (IOException e1) {
					log.error(e1.getMessage(), e1);
				}
			} catch (FileNotFoundException e2) {
				log.error(e2.getMessage());
			}
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
	}

	public void deleteImageFromFileSystem(Slika s) {
		for (int i = 0; i < size.length; i++) {
			String path = getImageFolderRealPath();
			path += File.separator + s.getImeSlike((String) size[i][2]);
			File sf = new File(path);
			sf.delete();
		}
	}

	private String getImageFolderRealPath() {
		return ((ServletContext) facesContext.getExternalContext().getContext())
				.getContext("/static").getRealPath("/images");
	}

	private String getThemeImageFolderRealPath() {
		return ((ServletContext) facesContext.getExternalContext().getContext())
				.getContext("/main").getRealPath("/resources/images");
	}

	private byte[] resizeImage(byte[] data, String contentType, int width,
			int height) {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

		boolean rescale = false;
		ImageIcon icon = new ImageIcon(data);
		// Ensure MAX IMAGE size is not breached
		width = Math.min(MAX_IMAGE_WIDTH, width);
		log.error("WIDTH: "+icon.getIconWidth()+" HEIGHT: "+icon.getIconHeight(), null);
		// Check if the image needs to be rescaled
		if (width > 0 && width != icon.getIconWidth()) {
			rescale = true;
			// if targeted size iz big picture prevent resizing small pictures
			if(MAX_IMAGE_WIDTH == width){
				if(icon.getIconWidth()<MIN_IMAGE_RESIZE_WIDTH)
					rescale = false;
				else if(icon.getIconWidth()>=MIN_IMAGE_RESIZE_WIDTH && icon.getIconWidth()<MAX_IMAGE_WIDTH && icon.getIconHeight()>MAX_IMAGE_RESIZE_HEIGHT){
					// picture width is good for widenning but to high to look good after transform
					log.error("IMAGE SHOULD NOT BE RESIZED!!!", null);
					rescale = false;
				}
			}
		}

		// Rescale the image if required
		if (rescale) {
			final double ratio = (double) width / icon.getIconWidth();
			height = height == -1 ? (int) (icon.getIconHeight() * ratio)
					: height;

			final int imageType = "image/png".equals(contentType) ? BufferedImage.TYPE_INT_ARGB
					: BufferedImage.TYPE_INT_RGB;
			final BufferedImage bImg = new BufferedImage(width, height,
					imageType);
			final Graphics2D g2d = bImg.createGraphics();
			g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
					RenderingHints.VALUE_INTERPOLATION_BICUBIC);
			g2d.drawImage(icon.getImage(), 0, 0, width, height, null);
			g2d.dispose();

			String formatName = "";
			if ("image/png".equals(contentType)) {
				formatName = "png";
			} else if ("image/jpeg".equals(contentType)
					|| "image/pjpeg".equals(contentType)) {
				formatName = "jpeg";
			} else if ("image/gif".equals(contentType)) {
				formatName = "gif";
			}

			try {
				ImageIO.write(bImg, formatName, outputStream);
			} catch (IOException e) {
				log.debug(e.getMessage(), e);
			}
		} else {
			try {
				outputStream.write(data);
			} catch (IOException e) {
				log.debug(e.getMessage(), e);
			}
		}

		data = outputStream.toByteArray();
		try {
			outputStream.close();
		} catch (IOException e) {
		}
		return data;

	}
}